/**
 * 区间求和，区间增加，区间开根号
 * 如果与D题一样，只做最小值，则可以每次增加一段以后再开根号，每次都要开根到单点操作，会T。
 * 注意到多次开根号以后，所有数值就变得相等，此时增加以后再开根号仍然是相等的，可以成段的更新和值。
 * 因此还记录一个最大值。
 * 但是如果只用 最小值 == 最大值 来判断是否可以区间开根号，仍然会T
 * 还有一个更强的条件，如果 (int)sqrt(最小值) - 最小值 == (int)sqrt(最大值) - 最大值
 * 就说明可以整段开根号，因为所有元素开根号以后减小的幅度是一样的。
 * 用了这个条件可以在474ms内跑完。
 * 
 * dn操作仍然只处理可以整段操作的
 * 
 * 开根号的modify，进入传统条件后，仍然分成三部分：
 * 1. 可以整段操作的
 * 2. 到底了要单点操作的
 * 3. 不是上述两种就继续去递归
 * 
 * 区间增加的modify直接用原始的写法即可
 * query操作直接用原始的写法即可
 */
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;

struct SegTree{ // 线段树带延迟

using llt = long long;

int N;

using value_type = array<llt, 3>; // 和, 最小值, 最大值
vector<value_type> data; // 线段树

using lazy_type = llt;
vector<lazy_type> lazy; // 延迟标记

/// 从下往上计算信息，要变动
value_type _up_(const value_type & ls, const value_type & rs) {
    // assert(0);
    return {ls[0] + rs[0], min(ls[1], rs[1]), max(ls[2], rs[2])};
}

/// 从上往下计算信息，要变动
void _dn_(int t, int s, int e, const lazy_type & delta) {
    // assert(0);
    data[t][0] += (e - s + 1) * delta;
    data[t][1] += delta;
    data[t][2] += delta;
    lazy[t] += delta;
}

/// 初始化，清零，不用动
void init(int n) {
    data.assign((N = n) + 1 << 2, value_zero());
    lazy.assign(n + 1 << 2, lazy_zero());
}

void modify(int a, int b){
    _modify(1, 1, N, a, b);
}

void modify(int a, int b, const lazy_type & delta){
    _modify(1, 1, N, a, b, delta);
}

/// 这个函数不用动
value_type query(int a, int b){
    return _query(1, 1, N, a, b);
}

/// 这个函数不用动
void build() {
    _build(1, 1, N);
}

/// 几乎不用动
value_type _query(int t, int s, int e, int a, int b) {
    if(a <= s and e <= b) {
        return data[t];
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    value_type ans = value_zero(); // 如果求最值，这里不能用zero
    if(a <= mid) ans = _up_(ans, _query(lson(t), s, mid, a, b));
    if(mid < b) ans = _up_(ans, _query(rson(t), mid + 1, e, a, b));
    return ans;
}

void _modify(int t, int s, int e, int a, int b) {
    if(a <= s and e <= b) {        
        long long su = sqrt(data[t][1]);
        long long sv = sqrt(data[t][2]);
        // if(data[t][1] == data[t][2]){ // 可以区间修改的
        if(su - sv == data[t][1] - data[t][2]){
            long long delta = su - data[t][1];
            _dn_(t, s, e, delta);
            return;
        }

        if(s == e){ // 需要单点修改的
            data[t][0] = data[t][1] = data[t][2] = sqrt(data[t][0]);
            return;
        }
        
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    if(a <= mid) _modify(lson(t), s, mid, a, b);
    if(mid < b) _modify(rson(t), mid + 1, e, a, b);
    _pushUp(t);
    return;
}

/// 几乎不用动
void _modify(int t, int s, int e, int a, int b, const lazy_type & delta) {
    if(a <= s and e <= b) {
        _dn_(t, s, e, delta);
        return;
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    if(a <= mid) _modify(lson(t), s, mid, a, b, delta);
    if(mid < b) _modify(rson(t), mid + 1, e, a, b, delta);
    _pushUp(t);
    return;
}

/// 这个函数不用动
void _pushUp(int t) {
    data[t] = _up_(data[lson(t)], data[rson(t)]);
}

/// 这个函数几乎不用动
void _pushDown(int t, int s, int e) {
    if(lazy_zero() == lazy[t]) return;
    auto & lz = lazy[t];
    auto ls = lson(t), rs = rson(t);
    int mid = (s + e) >> 1;

    _dn_(ls, s, mid, lz);
    _dn_(rs, mid + 1, e, lz);

    lz = lazy_zero();
}


/// 几乎不用动
void _build(int t, int s, int e) {
    if(s == e) {
        long long x; cin >> x;
        data[t] = {x, x, x}; // 注意提供value_type的初始化
        return; 
    }
    int mid = (s + e) >> 1;
    _build(lson(t), s, mid);
    _build(rson(t), mid + 1, e);
    _pushUp(t);
}

/// 辅助函数，视延迟的类型而变动
static const lazy_type & lazy_zero() {
    static const lazy_type LAZY0 = 0;
    return LAZY0; 
}

/// 辅助函数，视线段树信息类型而变动
static const value_type & value_zero() {
    static const value_type VALUE0 = {0LL, 0x1F2F3F4F5F6F7F8F, -0x1F2F3F4F5F6F7F8F};
    return VALUE0;
}

/// 这两个函数不用变动
static int lson(int x) {return x << 1;}
static int rson(int x) {return lson(x) | 1;}


};


int N;
int Q;	
SegTree St;

void work(){
    cin >> N >> Q;
    St.init(N);
    St.build();

    for(int c,a,b,x,q=1;q<=Q;++q){
        cin >> c >> a >> b;
        if(1 == c){
            St.modify(a, b);
        }else if(2 == c){
            cin >> x;
            St.modify(a, b, x);
        }else if(3 == c){
            auto ans = St.query(a, b);
            cout << ans[0] << "\n";            
        }else{
            // while(1);
            assert(0);
        }
    }
    return;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int nofkase = 1;
    // cin >> nofkase;
    while(nofkase--) work();
    return 0;
}